package com.itbaiyun.usercenter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itbaiyun.usercenter.common.ErrorCode;
import com.itbaiyun.usercenter.exception.BusinessException;
import com.itbaiyun.usercenter.mapper.TeamMapper;
import com.itbaiyun.usercenter.model.domain.Team;
import com.itbaiyun.usercenter.model.domain.User;
import com.itbaiyun.usercenter.model.domain.UserTeam;
import com.itbaiyun.usercenter.model.dto.TeamJoinDTO;
import com.itbaiyun.usercenter.model.dto.TeamQueryDTO;
import com.itbaiyun.usercenter.model.dto.TeamQuitDTO;
import com.itbaiyun.usercenter.model.dto.TeamUpdateDTO;
import com.itbaiyun.usercenter.model.enums.TeamStatusEnum;
import com.itbaiyun.usercenter.model.vo.TeamUserVO;
import com.itbaiyun.usercenter.model.vo.UserVO;
import com.itbaiyun.usercenter.service.TeamService;
import com.itbaiyun.usercenter.service.UserService;

import org.apache.commons.lang3.StringUtils;
import org.redisson.RedissonLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.*;

import static com.itbaiyun.usercenter.constant.RedisConstant.JOIN_TEAM_LOCK;

/**
 * @author O
 * @description 针对表【team(队伍)】的数据库操作Service实现
 * @createDate 2024-08-04 19:09:12
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
        implements TeamService {

    @Resource
    private UserTeamServiceImpl userTeamService;

    @Resource
    private UserService userService;

    @Resource
    private RedissonClient redissonClient;

//    @Resource
//    private TeamService teamService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public long addTeam(Team team, User loginUser) {
        long userId = loginUser.getId();
        //1. 请求参数校验
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR);
        }

        //2.是否登录
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NO_LOGIN);
        }

        //3.信息校验
        //  1. 队伍人数 > 1 且 <= 20
        int maxNum = team.getMaxNum();
        if (maxNum < 1 || maxNum > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //  2. 队伍标题 <= 20
        String name = team.getName();
        if (name.length() > 20 || StringUtils.isBlank(name)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //  3. 描述 <= 512
        String description = team.getDescription();
        if (description.length() > 512 || StringUtils.isBlank(description)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 4.状态是否公开
        int status = team.getStatus();
        //根据status获取枚举值
        TeamStatusEnum enumByCode = TeamStatusEnum.getEnumByCode(status);
        if (status < 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态不满足要求!");
        }

        //  5.如果status是加密状态,一定要有密码,且密码<=32
        if (enumByCode.equals(TeamStatusEnum.SECRET) && (StringUtils.isBlank(team.getPassword()) || team.getPassword().length() > 32)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍密码不满足要求!");
        }
        // 6.超时时间
        if (new Date().after(team.getExpireTime())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "超时时间,不满足要求!");
        }

        // 7.校验用户最多创建5个队伍
        //TODO 有bug-->疯狂点击100下
        QueryWrapper<Team> queryWrapper = new QueryWrapper();
        queryWrapper.eq("userId", userId);
        if (count(queryWrapper) >= 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建5个队伍!");
        }

        // 8.插入队伍信息到队伍表
        team.setId(null);
        team.setUserId(userId);
        boolean save = save(team);
        if (!save) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍插入失败");
        }
        // 9.插入用户队伍关系表
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(userId);
        userTeam.setTeamId(team.getId());
        userTeam.setJoinTime(new Date());
        boolean save1 = userTeamService.save(userTeam);
        if (!save1) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"系统内部异常");
        }
        return team.getId();
    }

    @Override
    public List<TeamUserVO> listTeams(TeamQueryDTO teamQueryDTO, boolean isAdmin) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        // 组合查询条件
        if (teamQueryDTO != null) {
            // 根据队伍id查询
            Long id = teamQueryDTO.getId();
            if (id != null && id > 0) {
                queryWrapper.eq("id", id);
            }
            // 根据关键词搜索
            String searchText = teamQueryDTO.getSearchText();
            if (searchText != null) {
                queryWrapper.and(qw -> qw.like("name", searchText).or().like("description", searchText));
            }
            // 根据描述信息查询
            String description = teamQueryDTO.getDescription();
            if (StringUtils.isNotBlank(description) && (description.length() > 0 && description.length() <= 512)) {
                queryWrapper.like("description", description);
            }
            //根据队伍名查询
            String name = teamQueryDTO.getName();
            if (StringUtils.isNotBlank(name)) {
                queryWrapper.like("name", name);
            }
            //根据队伍状态信息查询  不是管理员不能查询
            Integer status = teamQueryDTO.getStatus();
            TeamStatusEnum enumByCode = TeamStatusEnum.getEnumByCode(status);
            if (enumByCode == null) {
                enumByCode = TeamStatusEnum.PRIVATE;
            }
            if (!isAdmin && !enumByCode.equals(TeamStatusEnum.PRIVATE)) {
                throw new BusinessException(ErrorCode.NO_AUTH);
            }
            // 根据队伍最大人数查询
            Integer maxNum = teamQueryDTO.getMaxNum();
            if (maxNum != null && maxNum < 10) {
                queryWrapper.eq("maxNum", maxNum);
            }
            // 根据队长查询
            Long userId = teamQueryDTO.getUserId();
            if (userId != null) {
                queryWrapper.eq("userId", userId);
            }
        }
        // 已过期的队伍不查询
        queryWrapper.and(qw -> qw.gt("expireTime", new Date()).or().isNull("expireTime"));
        List<Team> teamList = this.list(queryWrapper);
        // 判空
        if (CollectionUtils.isEmpty(teamList)) {
            return Collections.emptyList();
        }
        // 关联查询创建人信息
        List<TeamUserVO> teamUserVOArrayList = new ArrayList<>();
        for (Team team : teamList) {
            Long userId = team.getUserId();
            User user = userService.getById(userId);
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);
            // 用户信息脱敏
            if (user != null) {
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user, userVO);
                teamUserVO.setCreateUser(userVO);
            }
            teamUserVOArrayList.add(teamUserVO);
        }
        return teamUserVOArrayList;
    }

    /**
     * 更新队伍实现类
     *
     * @param teamUpdateDTO 要更新的内容
     * @return 返回更新结果
     */
    @Override
    public boolean updateTeam(TeamUpdateDTO teamUpdateDTO, User loginUser) {
        // 参数判空
        if (teamUpdateDTO == null) {
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR);
        }
        Long id = teamUpdateDTO.getId();
        if (id < 0 || ObjectUtils.isEmpty(id)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        Team oldTeam = this.getById(id);
        if (oldTeam == null) {
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR, "队伍不存在");
        }

        // 根据id查出队伍队长
        Long userId = oldTeam.getUserId();
        // 判断是否为队长或者管理员
        if (userId != loginUser.getId() || !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        // 如果用户传入的信息是旧数据,不用更新 只要有一个不是旧数据就更新
        // 先从数据库中查出数据
        boolean isNew = false;
        String description = teamUpdateDTO.getDescription();
        if (!oldTeam.getDescription().equals(description)) {
            //不是旧数据  返回true
            isNew = true;
        }
        Date expireTime = teamUpdateDTO.getExpireTime();
        if (!oldTeam.getExpireTime().equals(expireTime)) {
            isNew = true;
        }
        String name = teamUpdateDTO.getName();
        if (!oldTeam.getName().equals(name)) {
            isNew = true;
        }
        String password = teamUpdateDTO.getPassword();
        if (!oldTeam.getPassword().equals(password)) {
            isNew = true;
        }
        Integer status = teamUpdateDTO.getStatus();
        if (!oldTeam.getStatus().equals(status)) {
            isNew = true;
        }
        if (isNew) {
            Team updateTeam = new Team();
            BeanUtils.copyProperties(teamUpdateDTO, updateTeam);
            boolean result = this.updateById(updateTeam);
            return result;
        }
        return true;
    }

    @Override
    public boolean joinTeam(TeamJoinDTO teamJoinDTO, User loginUser) {
        if (teamJoinDTO == null) {
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR);
        }
        // 用户最多加入五个队伍
        //根据用户id查出该登录用户加入了多少个队伍
        long userId = loginUser.getId();
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        long hasJoin = userTeamService.count(queryWrapper);
        if (hasJoin > 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "最多只能加入五个队伍!");
        }
        //分布式锁---->只有抢到锁的人才能加入队伍
        //尝试获得锁
        String key = JOIN_TEAM_LOCK;
        RLock rLock =redissonClient.getLock(key);
        boolean success = rLock.tryLock();
        while (true) {
            try {
                if (success) {
                    // 要加入的队伍id
                    Long teamJoinDTOId = teamJoinDTO.getId();
                    // 根据id查找队伍
                    Team team = this.getById(teamJoinDTOId);
                    //不能加入不存在的队伍
                    if (team == null) {
                        throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR, "队伍不存在!!");
                    }
                    //已过期的队伍不允许加入
                    Date expireTime = team.getExpireTime();
                    if (expireTime != null && new Date().after(expireTime)) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已过期");
                    }
                    // 加入私密队伍需要输入密码
                    Integer status = team.getStatus();
                    TeamStatusEnum enumByCode = TeamStatusEnum.getEnumByCode(status);
                    String password = teamJoinDTO.getPassword();
                    if (Objects.equals(enumByCode, TeamStatusEnum.SECRET)) {
                        if (!password.equals(team.getPassword()) || StringUtils.isBlank(password))
                            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码有误!");
                    }
                    // 已满人的队伍不能加入
                    queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("teamId", team.getId());
                    long joinTeamPeopleCount = userTeamService.count(queryWrapper);
                    if (joinTeamPeopleCount > team.getMaxNum()) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数已满!");
                    }
                    //不能重复加入已加入的队伍
                    queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("teamId", team.getId()).eq("userId", userId);
                    long count = userTeamService.count(queryWrapper);
                    if (count > 0) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "已加入该队伍");
                    }
                    // 修改队伍信息
                    UserTeam userTeam = new UserTeam();
                    userTeam.setTeamId(teamJoinDTOId);
                    userTeam.setUserId(userId);
                    userTeam.setJoinTime(new Date());
                    return userTeamService.save(userTeam);
                }
            } catch (BusinessException e) {
                log.error("加入队伍失败,", e);
            } finally {
                //释放锁
                //只能释放自己的锁
                if (rLock.isHeldByCurrentThread()) {
                    rLock.unlock();
                }
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean quitTeam(TeamQuitDTO teamQuiteDTO, User loginUser) {
        if (teamQuiteDTO == null) {
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR);
        }
        Long id = teamQuiteDTO.getId();

        //对id进行校验 ------->小于0的校验可以避免缓存穿透--->疯狂查询数据库不存在的数据
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 查看队伍是否存在
        Team team = this.getById(id);
        if (ObjectUtils.isEmpty(team)) {
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR, "队伍不存在!");
        }

        // 校验本登录用户是否加入队伍
        long userId = loginUser.getId();
        Long teamId = team.getId();
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId).eq("teamId", teamId);
        long count = userTeamService.count(queryWrapper);
        if (count <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户未加入该队伍!");
        }

        //如果队伍人数只有一个,直接解散队伍
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("teamId", teamId);
        long joinTeamPeopleCount = userTeamService.count(userTeamQueryWrapper);
        if (joinTeamPeopleCount == 1) {
            // 直接解散队伍
            this.removeById(teamId);
        } else {
            //队伍不止一个人,判断是否为队长
            if (team.getUserId().equals(userId)) {
                //是队长,退出后把权限转交给第二个人
                // 根据id升序查询出最早的两个用户,第一个就是队长,第二个是要转接权限的用户
                userTeamQueryWrapper = new QueryWrapper<>();
                userTeamQueryWrapper.eq("teamId", teamId).last("order by id asc limit 2");
                List<UserTeam> list = userTeamService.list(userTeamQueryWrapper);
                if (ObjectUtils.isEmpty(list) || list.size() <= 1) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }
                UserTeam nextUserTeam = list.get(1);
                Long nextUserTeamUserId = nextUserTeam.getUserId();
//                Team updateTeam = this.getById(teamId);
                Team updateTeam = new Team();
                updateTeam.setId(teamId);
                updateTeam.setUserId(nextUserTeamUserId);
                boolean result = this.updateById(updateTeam);
                if (!result) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "更新失败!");
                }
//                // 移除队伍和用户信息
//                queryWrapper = new QueryWrapper<>();
//                queryWrapper.eq("teamId", teamId);
            }
        }
        // 返回 删除队伍信息和队伍和用户关系信息
        return userTeamService.remove(queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteTeamByCaptain(TeamQuitDTO teamQuiteDTO,User loginUser) {
        if (teamQuiteDTO == null) {
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR);
        }
        // 不是队长不能解散
        long userId = loginUser.getId();
        Long teamQuiteDTOId = teamQuiteDTO.getId();
        Team team = this.getById(teamQuiteDTOId);
        Long userId1 = team.getUserId();
        if (!userId1.equals(userId)){
            throw new BusinessException(ErrorCode.NO_AUTH,"队长才能解散队伍!");
        }
        // 队伍是否存在
        if (ObjectUtils.isEmpty(team) || team == null){
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR,"队伍不存在");
        }
        //删除队伍
        boolean removeResult = this.removeById(team);
        if (!removeResult){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"删除失败!");
        }
        //移除所有队伍和用户信息
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId",team.getId());
        boolean remove = userTeamService.remove(queryWrapper);
        if (!remove){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"删除失败!");
        }
        return remove;
    }
}

